package wong.board.client;

import java.util.logging.Level;
import java.util.logging.Logger;

import wong.board.client.resources.BoardBundle;
import wong.board.client.util.DebugTool;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.GWT.UncaughtExceptionHandler;

/**
 * <p>
 * The class <code>Board</code> is the entry point of the application.
 * </p>
 * <p>
 * The key features are:
 * <ul>
 * <li>The {@link App} interface. An instance will be created via deferred
 * binding.</li>
 * <li>A static instance of the {@link BoardBundle}.</li>
 * <li>Some public constants: {@link Board#VERSION}, {@link Board#CONTENT_URL},
 * {@link Board#BODY_PANEL_ID}.</li>
 * </ul>
 * </p>
 * 
 * @author wong
 */
public class Board implements EntryPoint {
	private static final Logger logger = Logger.getLogger(Board.class.getName());
	/**
	 * the relative URL to the content data. (content.json)
	 */
	public static final String CONTENT_URL = "content.json";
	/**
	 * the id of the HTML element which will be used as the body element. (main)
	 */
	public static final String BODY_PANEL_ID = "main";
	public static final String VERSION = "1.0";
	private static ExceptionHandler exceptionHandler;
	private static Resources resources;

	public static BoardBundle getBundle() {
		if (resources == null) {
			resources = GWT.create(Resources.class);
		}
		return resources.getBundle();
	}

	public static ErrorHandler getErrorHandler() {
		return exceptionHandler;
	}

	private App app;

	/**
	 * This is the entry point method.
	 */
	public void onModuleLoad() {
		logger.config("Setting up exception handler.");
		exceptionHandler = new ExceptionHandler();
		GWT.setUncaughtExceptionHandler(exceptionHandler);
		logger.config("Starting the app...");
		startApp();
		logger.config("... app has been started.");
	}

	protected void startApp() {
		if (app == null) {
			logger.info("Starting...");
			try {
				logger.config("Creating app...");
				app = GWT.create(Board.App.class);
				logger.config("The app has been created. Setting up the app...");
				app.setup();
				logger.config("Seting up the app is done. Starting the app...");
				app.start();
				logger.config("The app is up and running.");
			} catch (Throwable th) {
				getErrorHandler().handleFatal("Cannot start the app.", th);
			} finally {
				logger.info("Start completed.");
			}
		} else {
			logger.warning("The app has already been started. This call will be ignored.");
		}
	}

	protected void stopApp() {
		if (app != null) {
			try {
				logger.info("Stopping the app...");
				app.stop();
				logger.config("The app has been stopped. Flushing the app...");
				app.flush();
				logger.config("The app has been flushed.");
			} catch (Throwable th) {
				getErrorHandler().handleError("Cannot stop the app.", th);
			} finally {
				app = null;
				logger.info("Stopped. You should reload the app. Good Bye!");
			}
		} else {
			logger.warning("The app has already been stopped. This call will be ignored.");
		}
	}

	/* */

	public interface App {

		void setup();

		void start();

		void stop();

		void flush();
	}

	public interface ErrorHandler {

		void handleError(String msg, Throwable error);

		void handleFatal(String msg, Throwable error);

	}

	private class ExceptionHandler implements ErrorHandler, UncaughtExceptionHandler {
		private Logger logger = Logger.getLogger(ExceptionHandler.class.getName());

		@Override
		public void handleError(String msg, Throwable error) {
			logger.log(Level.SEVERE, msg, error);
			DebugTool.showError(msg, error);
		}

		@Override
		public void handleFatal(String msg, Throwable error) {
			handleError(msg, error);
			logger.severe("Stopping the app...");
			stopApp();
			logger.config("... app has been stopped.");
		}

		@Override
		public void onUncaughtException(Throwable e) {
			handleFatal("Uncaught exception occured.", e);
		}

	}

}
